//
// RS Game Framework
// Copyright © 2010 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#ifndef RS_REFLECTION_METHOD_IPP
#define RS_REFLECTION_METHOD_IPP

#ifndef RS_REFLECTION_METHOD_IMPLEMENTATION_GUARD
#   error "Do not include this file directly. Instead, include rs/reflection/Method.hpp"
#endif

namespace rs
{
    namespace reflection
    {
        namespace internal
        {
            template< typename TMemberFunction > struct MemberFunctionTraits;

            template< typename TClass, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) () >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                static Boolean const kIsConstMethod = false;
                static UInt32 const kArgumentsCount = 0;
            };
            
            template< typename TClass, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) () const >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                static Boolean const kIsConstMethod = true;
                static UInt32 const kArgumentsCount = 0;
            };

            template< typename TClass, typename TArgument1, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1) >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                static Boolean const kIsConstMethod = false;
                static UInt32 const kArgumentsCount = 1;
            };
            
            template< typename TClass, typename TArgument1, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1) const >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                static Boolean const kIsConstMethod = true;
                static UInt32 const kArgumentsCount = 1;
            };

            template< typename TClass, typename TArgument1, typename TArgument2, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2) >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                static Boolean const kIsConstMethod = false;
                static UInt32 const kArgumentsCount = 2;
            };
            
            template< typename TClass, typename TArgument1, typename TArgument2, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2) const >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                static Boolean const kIsConstMethod = true;
                static UInt32 const kArgumentsCount = 2;
            };

            template< typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3) >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                typedef TArgument3 ArgumentThreeType;
                static Boolean const kIsConstMethod = false;
                static UInt32 const kArgumentsCount = 3;
            };
            
            template< typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3) const >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                typedef TArgument3 ArgumentThreeType;
                static Boolean const kIsConstMethod = true;
                static UInt32 const kArgumentsCount = 3;
            };

            template< typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4) >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                typedef TArgument3 ArgumentThreeType;
                typedef TArgument4 ArgumentFourType;
                static Boolean const kIsConstMethod = false;
                static UInt32 const kArgumentsCount = 4;
            };
            
            template< typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4) const >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                typedef TArgument3 ArgumentThreeType;
                typedef TArgument4 ArgumentFourType;
                static Boolean const kIsConstMethod = true;
                static UInt32 const kArgumentsCount = 4;
            };

            template< typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                typedef TArgument3 ArgumentThreeType;
                typedef TArgument4 ArgumentFourType;
                typedef TArgument5 ArgumentFiveType;
                static Boolean const kIsConstMethod = false;
                static UInt32 const kArgumentsCount = 5;
            };
            
            template< typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5, typename TReturn >
            struct MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) const >
            {
                typedef TReturn ReturnType;
                typedef TClass ClassType;
                typedef TArgument1 ArgumentOneType;
                typedef TArgument2 ArgumentTwoType;
                typedef TArgument3 ArgumentThreeType;
                typedef TArgument4 ArgumentFourType;
                typedef TArgument5 ArgumentFiveType;
                static Boolean const kIsConstMethod = true;
                static UInt32 const kArgumentsCount = 5;
            };
        }
        
        
        inline Method::Method() {
        }
        
        template< typename TReturn, typename TClass >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) ())
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1))
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2))
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3))
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4))
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5))
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) () const)
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1) const)
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2) const)
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3) const)
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4) const)
        : name_(name) {
            BindFunction(func);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
        inline Method::Method(Char8 const* name, TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) const)
        : name_(name) {
            BindFunction(func);
        }
        
        inline Method::Method(Method const& other)
        : argumentTypes_(other.argumentTypes_),
          any_(other.any_),
          name_(other.name_),
          returnType_(other.returnType_),
          isConst_(other.isConst_) {
        }
        
        template< typename TReturn, typename TClass >
        inline void Method::BindFunction(TReturn (TClass::*func) ()) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) () > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
        }
        
        template< typename TReturn, typename TClass, typename TArgument1 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1)) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2)) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3)) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentThreeType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4)) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentThreeType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentFourType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5)) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentThreeType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentFourType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentFiveType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass >
        inline void Method::BindFunction(TReturn (TClass::*func) () const) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) () > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
        }
        
        template< typename TReturn, typename TClass, typename TArgument1 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1) const) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2) const) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3) const) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentThreeType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4) const) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentThreeType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentFourType >::kQualifiedType);
        }
        
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
        inline void Method::BindFunction(TReturn (TClass::*func) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) const) {
            typedef typename internal::MemberFunctionTraits< TReturn (TClass::*) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5) > MethodTraitsType;
            any_.mfn_ptr = reinterpret_cast< rs::internal::generic_mfn_ptr >(func);
            isConst_ = MethodTraitsType::kIsConstMethod;
            returnType_ = ConvertToQualifiedType< typename MethodTraitsType::ReturnType >::kQualifiedType;
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentOneType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentTwoType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentThreeType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentFourType >::kQualifiedType);
            argumentTypes_.push_back(ConvertToQualifiedType< typename MethodTraitsType::ArgumentFiveType >::kQualifiedType);
        }

        // No arguments
        template< typename TClass, typename TReturn >
        inline void Method::operator()(TClass& obj, TReturn* out) const {
            RS_ASSERT(argumentTypes_.size() == 0, "Wrong number of arguments ");                
            typedef TReturn (TClass::*FunctionType) ();
            
            if (HasNonVoidReturnType() && out != 0) {
                *out = (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))();
            }
            else {
                (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))();
            }
        }

        // 1 argument
        template< typename TReturn, typename TClass, typename TArgument1 >
        inline void Method::operator()(TClass& obj, TArgument1 arg1, TReturn* out) const {
            RS_ASSERT(argumentTypes_.size() == 1, "Wrong number of arguments ");                
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[0], ConvertToQualifiedType< TArgument1 >::kQualifiedType), "Wrong argument type");
            typedef TReturn (TClass::*FunctionType) (TArgument1);
            
            if (HasNonVoidReturnType() && out != 0) {
                *out = (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1);
            }
            else {
                (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1);
            }            
        }

        // 2 arguments
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2 >
        inline void Method::operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TReturn* out) const {
            RS_ASSERT(argumentTypes_.size() == 2, "Wrong number of arguments ");                
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[0], ConvertToQualifiedType< TArgument1 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[1], ConvertToQualifiedType< TArgument2 >::kQualifiedType), "Wrong argument type");
            typedef TReturn (TClass::*FunctionType) (TArgument1, TArgument2);
            
            if (HasNonVoidReturnType() && out != 0) {
                *out = (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2);
            }
            else {
                (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2);
            }            
        }

        // 3 arguments
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3 >
        inline void Method::operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TArgument3 arg3, TReturn* out) const {
            RS_ASSERT(argumentTypes_.size() == 3, "Wrong number of arguments ");                
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[0], ConvertToQualifiedType< TArgument1 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[1], ConvertToQualifiedType< TArgument2 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[2], ConvertToQualifiedType< TArgument3 >::kQualifiedType), "Wrong argument type");
            typedef TReturn (TClass::*FunctionType) (TArgument1, TArgument2, TArgument3);
            
            if (HasNonVoidReturnType() && out != 0) {
                *out = (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2, arg3);
            }
            else {
                (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2, arg3);
            }            
        }

        // 4 arguments
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4 >
        inline void Method::operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TArgument3 arg3, TArgument4 arg4, TReturn* out) const {
            RS_ASSERT(argumentTypes_.size() == 4, "Wrong number of arguments ");                
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[0], ConvertToQualifiedType< TArgument1 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[1], ConvertToQualifiedType< TArgument2 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[2], ConvertToQualifiedType< TArgument3 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[3], ConvertToQualifiedType< TArgument4 >::kQualifiedType), "Wrong argument type");
            typedef TReturn (TClass::*FunctionType) (TArgument1, TArgument2, TArgument3, TArgument4);
            
            if (HasNonVoidReturnType() && out != 0) {
                *out = (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2, arg3, arg4);
            }
            else {
                (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2, arg3, arg4);
            }            
        }  

        // 5 arguments
        template< typename TReturn, typename TClass, typename TArgument1, typename TArgument2, typename TArgument3, typename TArgument4, typename TArgument5 >
        inline void Method::operator()(TClass& obj, TArgument1 arg1, TArgument2 arg2, TArgument3 arg3, TArgument4 arg4, TArgument5 arg5, TReturn* out) const {
            RS_ASSERT(argumentTypes_.size() == 5, "Wrong number of arguments ");                
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[0], ConvertToQualifiedType< TArgument1 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[1], ConvertToQualifiedType< TArgument2 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[2], ConvertToQualifiedType< TArgument3 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[3], ConvertToQualifiedType< TArgument4 >::kQualifiedType), "Wrong argument type");
            RS_ASSERT(CompareArgumentTypes(argumentTypes_[4], ConvertToQualifiedType< TArgument5 >::kQualifiedType), "Wrong argument type");
            typedef TReturn (TClass::*FunctionType) (TArgument1, TArgument2, TArgument3, TArgument4, TArgument5);
            
            if (HasNonVoidReturnType() && out != 0) {
                *out = (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr) )(arg1, arg2, arg3, arg4, arg5);
            }
            else {
                (obj.*reinterpret_cast< FunctionType >(any_.mfn_ptr))(arg1, arg2, arg3, arg4, arg5);
            }            
        }
        
        inline Char8 const* Method::GetName() const {
            return name_;
        }
        
        inline Type Method::GetReturnType() const {
            return Type(returnType_);
        }
        
        inline UInt64 Method::GetRawReturnType() const {
            return returnType_;
        }
        
        inline Type Method::GetArgumentType(UInt32 i) const {
            RS_ASSERT(i < argumentTypes_.size(), "requested argument index, 'i', is out of range");
            return Type(argumentTypes_[i]);
        }
        
        inline UInt64 Method::GetRawArgumentType(UInt32 i) const {
            RS_ASSERT(i < argumentTypes_.size(), "requested argument index, 'i', is out of range");
            return argumentTypes_[i];
        }
        
        inline RsSize Method::GetArgumentCount() const {
            return argumentTypes_.size();
        }
        
        inline Boolean Method::HasNonVoidReturnType() const {
            return (ExtractType(returnType_) != Types::Void);
        }
        
        inline Boolean Method::operator==(Method const& other) const {
            if (!(any_.mfn_ptr == other.any_.mfn_ptr &&
                isConst_ == other.isConst_ &&
                returnType_ == other.returnType_ &&
                std::strcmp(name_, other.GetName()) == 0))
            {
                return false;
            }
            
            UInt32 index = 0;
            for(std::vector< UInt64 >::const_iterator itr = argumentTypes_.begin(); itr != argumentTypes_.end(); ++itr, ++index) {
                if (*itr != other.argumentTypes_[index]) {
                    return false;
                }
            }
            
            return true;
        }
        
        inline Boolean Method::operator!=(Method const& other) const {
            return !(*this == other);
        }
    }
}

#endif // RS_REFLECTION_METHOD_IPP
